package com.gitee.wsl.ext.math

import com.gitee.wsl.ext.base.DoubleConsts
import com.gitee.wsl.ext.base.FloatConsts
import com.gitee.wsl.ext.base.MAX_EXPONENT
import com.gitee.wsl.ext.base.MIN_EXPONENT
import com.gitee.wsl.ext.base.doubleToRawLongBits
import com.gitee.wsl.ext.base.floatToRawIntBits
import com.gitee.wsl.ext.base.intBitsToFloat
import com.gitee.wsl.ext.base.isFinite
import com.gitee.wsl.ext.base.isInfinite
import com.gitee.wsl.ext.base.isNaN
import com.gitee.wsl.ext.base.longBitsToDouble
import com.gitee.wsl.math.Math
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlin.math.abs


/**
 * Returns the sum of its arguments,
 * throwing an exception if the result overflows an `int`.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @throws ArithmeticException if the result overflows an int
 * @since 1.8
 */

fun Math.addExact(x: Int, y: Int): Int {
    val r = x + y
    // HD 2-12 Overflow iff both arguments have the opposite sign of the result
    if (((x xor r) and (y xor r)) < 0) {
        throw ArithmeticException("integer overflow")
    }
    return r
}

/**
 * Returns the sum of its arguments,
 * throwing an exception if the result overflows a `long`.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 1.8
 */

fun Math.addExact(x: Long, y: Long): Long {
    val r = x + y
    // HD 2-12 Overflow iff both arguments have the opposite sign of the result
    if (((x xor r) and (y xor r)) < 0) {
        throw ArithmeticException("long overflow")
    }
    return r
}

/**
 * Returns the difference of the arguments,
 * throwing an exception if the result overflows an `int`.
 *
 * @param x the first value
 * @param y the second value to subtract from the first
 * @return the result
 * @throws ArithmeticException if the result overflows an int
 * @since 1.8
 */

fun Math.subtractExact(x: Int, y: Int): Int {
    val r = x - y
    // HD 2-12 Overflow iff the arguments have different signs and
    // the sign of the result is different from the sign of x
    if (((x xor y) and (x xor r)) < 0) {
        throw ArithmeticException("integer overflow")
    }
    return r
}

/**
 * Returns the difference of the arguments,
 * throwing an exception if the result overflows a `long`.
 *
 * @param x the first value
 * @param y the second value to subtract from the first
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 1.8
 */

fun Math.subtractExact(x: Long, y: Long): Long {
    val r = x - y
    // HD 2-12 Overflow iff the arguments have different signs and
    // the sign of the result is different from the sign of x
    if (((x xor y) and (x xor r)) < 0) {
        throw ArithmeticException("long overflow")
    }
    return r
}

/**
 * Returns the product of the arguments,
 * throwing an exception if the result overflows an `int`.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @throws ArithmeticException if the result overflows an int
 * @since 1.8
 */

fun Math.multiplyExact(x: Int, y: Int): Int {
    val r = x.toLong() * y.toLong()
    if (r.toInt().toLong() != r) {
        throw ArithmeticException("integer overflow")
    }
    return r.toInt()
}

/**
 * Returns the product of the arguments, throwing an exception if the result
 * overflows a `long`.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 9
 */
fun Math.multiplyExact(x: Long, y: Int): Long {
    return multiplyExact(x, y.toLong())
}

/**
 * Returns the product of the arguments,
 * throwing an exception if the result overflows a `long`.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 1.8
 */

fun Math.multiplyExact(x: Long, y: Long): Long {
    val r = x * y
    val ax = abs(x)
    val ay = abs(y)
    if (((ax or ay) ushr 31 != 0L)) {
        // Some bits greater than 2^31 that might cause overflow
        // Check the result using the divide operator
        // and check for the special case of Long.MIN_VALUE * -1
        if (((y != 0L) && (r / y != x)) ||
            (x == Long.Companion.MIN_VALUE && y == -1L)
        ) {
            throw ArithmeticException("long overflow")
        }
    }
    return r
}

/**
 * Returns the quotient of the arguments, throwing an exception if the
 * result overflows an `int`.  Such overflow occurs in this method if
 * `x` is [Integer.MIN_VALUE] and `y` is `-1`.
 * In contrast, if `Integer.MIN_VALUE / -1` were evaluated directly,
 * the result would be `Integer.MIN_VALUE` and no exception would be
 * thrown.
 *
 *
 * If `y` is zero, an `ArithmeticException` is thrown
 * (JLS {@jls 15.17.2}).
 *
 *
 * The built-in remainder operator "`%`" is a suitable counterpart
 * both for this method and for the built-in division operator "`/`".
 *
 * @param x the dividend
 * @param y the divisor
 * @return the quotient `x / y`
 * @throws ArithmeticException if `y` is zero or the quotient
 * overflows an int
 * @jls 15.17.2 Division Operator /
 * @since 18
 */
fun Math.divideExact(x: Int, y: Int): Int {
    val q = x / y
    if ((x and y and q) >= 0) {
        return q
    }
    throw ArithmeticException("integer overflow")
}

/**
 * Returns the quotient of the arguments, throwing an exception if the
 * result overflows a `long`.  Such overflow occurs in this method if
 * `x` is [Long.MIN_VALUE] and `y` is `-1`.
 * In contrast, if `Long.MIN_VALUE / -1` were evaluated directly,
 * the result would be `Long.MIN_VALUE` and no exception would be
 * thrown.
 *
 *
 * If `y` is zero, an `ArithmeticException` is thrown
 * (JLS {@jls 15.17.2}).
 *
 *
 * The built-in remainder operator "`%`" is a suitable counterpart
 * both for this method and for the built-in division operator "`/`".
 *
 * @param x the dividend
 * @param y the divisor
 * @return the quotient `x / y`
 * @throws ArithmeticException if `y` is zero or the quotient
 * overflows a long
 * @jls 15.17.2 Division Operator /
 * @since 18
 */
fun Math.divideExact(x: Long, y: Long): Long {
    val q = x / y
    if ((x and y and q) >= 0) {
        return q
    }
    throw ArithmeticException("long overflow")
}

/**
 * Returns the largest (closest to positive infinity)
 * `int` value that is less than or equal to the algebraic quotient.
 * This method is identical to [.floorDiv] except that it
 * throws an `ArithmeticException` when the dividend is
 * [Integer.MIN_VALUE] and the divisor is
 * `-1` instead of ignoring the integer overflow and returning
 * `Integer.MIN_VALUE`.
 *
 *
 * The floor modulus method [.floorMod] is a suitable
 * counterpart both for this method and for the [.floorDiv]
 * method.
 *
 *
 * For examples, see [.floorDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the largest (closest to positive infinity)
 * `int` value that is less than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero, or the
 * dividend `x` is `Integer.MIN_VALUE` and the divisor `y`
 * is `-1`.
 * @see .floorDiv
 * @since 18
 */
fun Math.floorDivExact(x: Int, y: Int): Int {
    val q = x / y
    if ((x and y and q) >= 0) {
        // if the signs are different and modulo not zero, round down
        if ((x xor y) < 0 && (q * y != x)) {
            return q - 1
        }
        return q
    }
    throw ArithmeticException("integer overflow")
}

/**
 * Returns the largest (closest to positive infinity)
 * `long` value that is less than or equal to the algebraic quotient.
 * This method is identical to [.floorDiv] except that it
 * throws an `ArithmeticException` when the dividend is
 * [Long.MIN_VALUE] and the divisor is
 * `-1` instead of ignoring the integer overflow and returning
 * `Long.MIN_VALUE`.
 *
 *
 * The floor modulus method [.floorMod] is a suitable
 * counterpart both for this method and for the [.floorDiv]
 * method.
 *
 *
 * For examples, see [.floorDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the largest (closest to positive infinity)
 * `long` value that is less than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero, or the
 * dividend `x` is `Long.MIN_VALUE` and the divisor `y`
 * is `-1`.
 * @see .floorDiv
 * @since 18
 */
fun Math.floorDivExact(x: Long, y: Long): Long {
    val q = x / y
    if ((x and y and q) >= 0) {
        // if the signs are different and modulo not zero, round down
        if ((x xor y) < 0 && (q * y != x)) {
            return q - 1
        }
        return q
    }
    throw ArithmeticException("long overflow")
}

/**
 * Returns the smallest (closest to negative infinity)
 * `int` value that is greater than or equal to the algebraic quotient.
 * This method is identical to [.ceilDiv] except that it
 * throws an `ArithmeticException` when the dividend is
 * [Integer.MIN_VALUE] and the divisor is
 * `-1` instead of ignoring the integer overflow and returning
 * `Integer.MIN_VALUE`.
 *
 *
 * The ceil modulus method [.ceilMod] is a suitable
 * counterpart both for this method and for the [.ceilDiv]
 * method.
 *
 *
 * For examples, see [.ceilDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the smallest (closest to negative infinity)
 * `int` value that is greater than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero, or the
 * dividend `x` is `Integer.MIN_VALUE` and the divisor `y`
 * is `-1`.
 * @see .ceilDiv
 * @since 18
 */
fun Math.ceilDivExact(x: Int, y: Int): Int {
    val q = x / y
    if ((x and y and q) >= 0) {
        // if the signs are the same and modulo not zero, round up
        if ((x xor y) >= 0 && (q * y != x)) {
            return q + 1
        }
        return q
    }
    throw ArithmeticException("integer overflow")
}

/**
 * Returns the smallest (closest to negative infinity)
 * `long` value that is greater than or equal to the algebraic quotient.
 * This method is identical to [.ceilDiv] except that it
 * throws an `ArithmeticException` when the dividend is
 * [Long.MIN_VALUE] and the divisor is
 * `-1` instead of ignoring the integer overflow and returning
 * `Long.MIN_VALUE`.
 *
 *
 * The ceil modulus method [.ceilMod] is a suitable
 * counterpart both for this method and for the [.ceilDiv]
 * method.
 *
 *
 * For examples, see [.ceilDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the smallest (closest to negative infinity)
 * `long` value that is greater than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero, or the
 * dividend `x` is `Long.MIN_VALUE` and the divisor `y`
 * is `-1`.
 * @see .ceilDiv
 * @since 18
 */
fun Math.ceilDivExact(x: Long, y: Long): Long {
    val q = x / y
    if ((x and y and q) >= 0) {
        // if the signs are the same and modulo not zero, round up
        if ((x xor y) >= 0 && (q * y != x)) {
            return q + 1
        }
        return q
    }
    throw ArithmeticException("long overflow")
}

/**
 * Returns the argument incremented by one, throwing an exception if the
 * result overflows an `int`.
 * The overflow only occurs for [the maximum value][Integer.MAX_VALUE].
 *
 * @param a the value to increment
 * @return the result
 * @throws ArithmeticException if the result overflows an int
 * @since 1.8
 */

fun Math.incrementExact(a: Int): Int {
    if (a == Int.Companion.MAX_VALUE) {
        throw ArithmeticException("integer overflow")
    }

    return a + 1
}

/**
 * Returns the argument incremented by one, throwing an exception if the
 * result overflows a `long`.
 * The overflow only occurs for [the maximum value][Long.MAX_VALUE].
 *
 * @param a the value to increment
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 1.8
 */

fun Math.incrementExact(a: Long): Long {
    if (a == Long.Companion.MAX_VALUE) {
        throw ArithmeticException("long overflow")
    }

    return a + 1L
}

/**
 * Returns the argument decremented by one, throwing an exception if the
 * result overflows an `int`.
 * The overflow only occurs for [the minimum value][Integer.MIN_VALUE].
 *
 * @param a the value to decrement
 * @return the result
 * @throws ArithmeticException if the result overflows an int
 * @since 1.8
 */

fun Math.decrementExact(a: Int): Int {
    if (a == Int.Companion.MIN_VALUE) {
        throw ArithmeticException("integer overflow")
    }

    return a - 1
}

/**
 * Returns the argument decremented by one, throwing an exception if the
 * result overflows a `long`.
 * The overflow only occurs for [the minimum value][Long.MIN_VALUE].
 *
 * @param a the value to decrement
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 1.8
 */

fun Math.decrementExact(a: Long): Long {
    if (a == Long.Companion.MIN_VALUE) {
        throw ArithmeticException("long overflow")
    }

    return a - 1L
}

/**
 * Returns the negation of the argument, throwing an exception if the
 * result overflows an `int`.
 * The overflow only occurs for [the minimum value][Integer.MIN_VALUE].
 *
 * @param a the value to negate
 * @return the result
 * @throws ArithmeticException if the result overflows an int
 * @since 1.8
 */

fun Math.negateExact(a: Int): Int {
    if (a == Int.Companion.MIN_VALUE) {
        throw ArithmeticException("integer overflow")
    }

    return -a
}

/**
 * Returns the negation of the argument, throwing an exception if the
 * result overflows a `long`.
 * The overflow only occurs for [the minimum value][Long.MIN_VALUE].
 *
 * @param a the value to negate
 * @return the result
 * @throws ArithmeticException if the result overflows a long
 * @since 1.8
 */

fun Math.negateExact(a: Long): Long {
    if (a == Long.Companion.MIN_VALUE) {
        throw ArithmeticException("long overflow")
    }

    return -a
}

/**
 * Returns the value of the `long` argument,
 * throwing an exception if the value overflows an `int`.
 *
 * @param value the long value
 * @return the argument as an int
 * @throws ArithmeticException if the `argument` overflows an int
 * @since 1.8
 */
fun Math.toIntExact(value: Long): Int {
    if (value.toInt().toLong() != value) {
        throw ArithmeticException("integer overflow")
    }
    return value.toInt()
}

/**
 * Returns the exact mathematical product of the arguments.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @since 9
 */
fun Math.multiplyFull(x: Int, y: Int): Long {
    return x.toLong() * y.toLong()
}

/**
 * Returns as a `long` the most significant 64 bits of the 128-bit
 * product of two 64-bit factors.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @see .unsignedMultiplyHigh
 *
 * @since 9
 */

fun Math.multiplyHigh(x: Long, y: Long): Long {
    // Use technique from section 8-2 of Henry S. Warren, Jr.,
    // Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174.
    val x1 = x shr 32
    val x2 = x and 0xFFFFFFFFL
    val y1 = y shr 32
    val y2 = y and 0xFFFFFFFFL

    val z2 = x2 * y2
    val t = x1 * y2 + (z2 ushr 32)
    var z1 = t and 0xFFFFFFFFL
    val z0 = t shr 32
    z1 += x2 * y1

    return x1 * y1 + z0 + (z1 shr 32)
}

/**
 * Returns as a `long` the most significant 64 bits of the unsigned
 * 128-bit product of two unsigned 64-bit factors.
 *
 * @param x the first value
 * @param y the second value
 * @return the result
 * @see .multiplyHigh
 *
 * @since 18
 */

fun Math.unsignedMultiplyHigh(x: Long, y: Long): Long {
    // Compute via multiplyHigh() to leverage the intrinsic
    var result: Long = multiplyHigh(x, y)
    result += (y and (x shr 63)) // equivalent to `if (x < 0) result += y;`
    result += (x and (y shr 63)) // equivalent to `if (y < 0) result += x;`
    return result
}

/**
 * Returns the largest (closest to positive infinity)
 * `int` value that is less than or equal to the algebraic quotient.
 * There is one special case: if the dividend is
 * [Integer.MIN_VALUE] and the divisor is `-1`,
 * then integer overflow occurs and
 * the result is equal to `Integer.MIN_VALUE`.
 *
 *
 * Normal integer division operates under the round to zero rounding mode
 * (truncation).  This operation instead acts under the round toward
 * negative infinity (floor) rounding mode.
 * The floor rounding mode gives different results from truncation
 * when the exact quotient is not an integer and is negative.
 *
 *  * If the signs of the arguments are the same, the results of
 * `floorDiv` and the `/` operator are the same.  <br></br>
 * For example, `floorDiv(4, 3) == 1` and `(4 / 3) == 1`.
 *  * If the signs of the arguments are different, `floorDiv`
 * returns the largest integer less than or equal to the quotient
 * while the `/` operator returns the smallest integer greater
 * than or equal to the quotient.
 * They differ if and only if the quotient is not an integer.<br></br>
 * For example, `floorDiv(-4, 3) == -2`,
 * whereas `(-4 / 3) == -1`.
 *
 *
 *
 * @param x the dividend
 * @param y the divisor
 * @return the largest (closest to positive infinity)
 * `int` value that is less than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .floorMod
 * @see .floor
 * @since 1.8
 */
fun Math.floorDiv(x: Int, y: Int): Int {
    val q = x / y
    // if the signs are different and modulo not zero, round down
    if ((x xor y) < 0 && (q * y != x)) {
        return q - 1
    }
    return q
}

/**
 * Returns the largest (closest to positive infinity)
 * `long` value that is less than or equal to the algebraic quotient.
 * There is one special case: if the dividend is
 * [Long.MIN_VALUE] and the divisor is `-1`,
 * then integer overflow occurs and
 * the result is equal to `Long.MIN_VALUE`.
 *
 *
 * Normal integer division operates under the round to zero rounding mode
 * (truncation).  This operation instead acts under the round toward
 * negative infinity (floor) rounding mode.
 * The floor rounding mode gives different results from truncation
 * when the exact result is not an integer and is negative.
 *
 *
 * For examples, see [.floorDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the largest (closest to positive infinity)
 * `long` value that is less than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .floorMod
 * @see .floor
 * @since 9
 */
fun Math.floorDiv(x: Long, y: Int): Long {
    return floorDiv(x, y.toLong())
}

/**
 * Returns the largest (closest to positive infinity)
 * `long` value that is less than or equal to the algebraic quotient.
 * There is one special case: if the dividend is
 * [Long.MIN_VALUE] and the divisor is `-1`,
 * then integer overflow occurs and
 * the result is equal to `Long.MIN_VALUE`.
 *
 *
 * Normal integer division operates under the round to zero rounding mode
 * (truncation).  This operation instead acts under the round toward
 * negative infinity (floor) rounding mode.
 * The floor rounding mode gives different results from truncation
 * when the exact result is not an integer and is negative.
 *
 *
 * For examples, see [.floorDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the largest (closest to positive infinity)
 * `long` value that is less than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .floorMod
 * @see .floor
 * @since 1.8
 */
fun Math.floorDiv(x: Long, y: Long): Long {
    val q = x / y
    // if the signs are different and modulo not zero, round down
    if ((x xor y) < 0 && (q * y != x)) {
        return q - 1
    }
    return q
}

/**
 * Returns the floor modulus of the `int` arguments.
 *
 *
 * The floor modulus is `r = x - (floorDiv(x, y) * y)`,
 * has the same sign as the divisor `y` or is zero, and
 * is in the range of `-abs(y) < r < +abs(y)`.
 *
 *
 *
 * The relationship between `floorDiv` and `floorMod` is such that:
 *
 *  * `floorDiv(x, y) * y + floorMod(x, y) == x`
 *
 *
 *
 * The difference in values between `floorMod` and the `%` operator
 * is due to the difference between `floorDiv` and the `/`
 * operator, as detailed in [.floorDiv].
 *
 *
 * Examples:
 *
 *  * Regardless of the signs of the arguments, `floorMod`(x, y)
 * is zero exactly when `x % y` is zero as well.
 *  * If neither `floorMod`(x, y) nor `x % y` is zero,
 * they differ exactly when the signs of the arguments differ.<br></br>
 *
 *  * `floorMod(+4, +3) == +1`; &nbsp; and `(+4 % +3) == +1`
 *  * `floorMod(-4, -3) == -1`; &nbsp; and `(-4 % -3) == -1`
 *  * `floorMod(+4, -3) == -2`; &nbsp; and `(+4 % -3) == +1`
 *  * `floorMod(-4, +3) == +2`; &nbsp; and `(-4 % +3) == -1`
 *
 *
 *
 *
 * @param x the dividend
 * @param y the divisor
 * @return the floor modulus `x - (floorDiv(x, y) * y)`
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .floorDiv
 * @since 1.8
 */
fun Math.floorMod(x: Int, y: Int): Int {
    val r = x % y
    // if the signs are different and modulo not zero, adjust result
    if ((x xor y) < 0 && r != 0) {
        return r + y
    }
    return r
}

/**
 * Returns the floor modulus of the `long` and `int` arguments.
 *
 *
 * The floor modulus is `r = x - (floorDiv(x, y) * y)`,
 * has the same sign as the divisor `y` or is zero, and
 * is in the range of `-abs(y) < r < +abs(y)`.
 *
 *
 *
 * The relationship between `floorDiv` and `floorMod` is such that:
 *
 *  * `floorDiv(x, y) * y + floorMod(x, y) == x`
 *
 *
 *
 * For examples, see [.floorMod].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the floor modulus `x - (floorDiv(x, y) * y)`
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .floorDiv
 * @since 9
 */
fun Math.floorMod(x: Long, y: Int): Int {
    // Result cannot overflow the range of int.
    return floorMod(x, y.toLong()).toInt()
}

/**
 * Returns the floor modulus of the `long` arguments.
 *
 *
 * The floor modulus is `r = x - (floorDiv(x, y) * y)`,
 * has the same sign as the divisor `y` or is zero, and
 * is in the range of `-abs(y) < r < +abs(y)`.
 *
 *
 *
 * The relationship between `floorDiv` and `floorMod` is such that:
 *
 *  * `floorDiv(x, y) * y + floorMod(x, y) == x`
 *
 *
 *
 * For examples, see [.floorMod].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the floor modulus `x - (floorDiv(x, y) * y)`
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .floorDiv
 * @since 1.8
 */
fun Math.floorMod(x: Long, y: Long): Long {
    val r = x % y
    // if the signs are different and modulo not zero, adjust result
    if ((x xor y) < 0 && r != 0L) {
        return r + y
    }
    return r
}

/**
 * Returns the smallest (closest to negative infinity)
 * `int` value that is greater than or equal to the algebraic quotient.
 * There is one special case: if the dividend is
 * [Integer.MIN_VALUE] and the divisor is `-1`,
 * then integer overflow occurs and
 * the result is equal to `Integer.MIN_VALUE`.
 *
 *
 * Normal integer division operates under the round to zero rounding mode
 * (truncation).  This operation instead acts under the round toward
 * positive infinity (ceiling) rounding mode.
 * The ceiling rounding mode gives different results from truncation
 * when the exact quotient is not an integer and is positive.
 *
 *  * If the signs of the arguments are different, the results of
 * `ceilDiv` and the `/` operator are the same.  <br></br>
 * For example, `ceilDiv(-4, 3) == -1` and `(-4 / 3) == -1`.
 *  * If the signs of the arguments are the same, `ceilDiv`
 * returns the smallest integer greater than or equal to the quotient
 * while the `/` operator returns the largest integer less
 * than or equal to the quotient.
 * They differ if and only if the quotient is not an integer.<br></br>
 * For example, `ceilDiv(4, 3) == 2`,
 * whereas `(4 / 3) == 1`.
 *
 *
 *
 * @param x the dividend
 * @param y the divisor
 * @return the smallest (closest to negative infinity)
 * `int` value that is greater than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .ceilMod
 * @see .ceil
 * @since 18
 */
fun Math.ceilDiv(x: Int, y: Int): Int {
    val q = x / y
    // if the signs are the same and modulo not zero, round up
    if ((x xor y) >= 0 && (q * y != x)) {
        return q + 1
    }
    return q
}

/**
 * Returns the smallest (closest to negative infinity)
 * `long` value that is greater than or equal to the algebraic quotient.
 * There is one special case: if the dividend is
 * [Long.MIN_VALUE] and the divisor is `-1`,
 * then integer overflow occurs and
 * the result is equal to `Long.MIN_VALUE`.
 *
 *
 * Normal integer division operates under the round to zero rounding mode
 * (truncation).  This operation instead acts under the round toward
 * positive infinity (ceiling) rounding mode.
 * The ceiling rounding mode gives different results from truncation
 * when the exact result is not an integer and is positive.
 *
 *
 * For examples, see [.ceilDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the smallest (closest to negative infinity)
 * `long` value that is greater than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .ceilMod
 * @see .ceil
 * @since 18
 */
fun Math.ceilDiv(x: Long, y: Int): Long {
    return ceilDiv(x, y.toLong())
}

/**
 * Returns the smallest (closest to negative infinity)
 * `long` value that is greater than or equal to the algebraic quotient.
 * There is one special case: if the dividend is
 * [Long.MIN_VALUE] and the divisor is `-1`,
 * then integer overflow occurs and
 * the result is equal to `Long.MIN_VALUE`.
 *
 *
 * Normal integer division operates under the round to zero rounding mode
 * (truncation).  This operation instead acts under the round toward
 * positive infinity (ceiling) rounding mode.
 * The ceiling rounding mode gives different results from truncation
 * when the exact result is not an integer and is positive.
 *
 *
 * For examples, see [.ceilDiv].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the smallest (closest to negative infinity)
 * `long` value that is greater than or equal to the algebraic quotient.
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .ceilMod
 * @see .ceil
 * @since 18
 */
fun Math.ceilDiv(x: Long, y: Long): Long {
    val q = x / y
    // if the signs are the same and modulo not zero, round up
    if ((x xor y) >= 0 && (q * y != x)) {
        return q + 1
    }
    return q
}

/**
 * Returns the ceiling modulus of the `int` arguments.
 *
 *
 * The ceiling modulus is `r = x - (ceilDiv(x, y) * y)`,
 * has the opposite sign as the divisor `y` or is zero, and
 * is in the range of `-abs(y) < r < +abs(y)`.
 *
 *
 *
 * The relationship between `ceilDiv` and `ceilMod` is such that:
 *
 *  * `ceilDiv(x, y) * y + ceilMod(x, y) == x`
 *
 *
 *
 * The difference in values between `ceilMod` and the `%` operator
 * is due to the difference between `ceilDiv` and the `/`
 * operator, as detailed in [.ceilDiv].
 *
 *
 * Examples:
 *
 *  * Regardless of the signs of the arguments, `ceilMod`(x, y)
 * is zero exactly when `x % y` is zero as well.
 *  * If neither `ceilMod`(x, y) nor `x % y` is zero,
 * they differ exactly when the signs of the arguments are the same.<br></br>
 *
 *  * `ceilMod(+4, +3) == -2`; &nbsp; and `(+4 % +3) == +1`
 *  * `ceilMod(-4, -3) == +2`; &nbsp; and `(-4 % -3) == -1`
 *  * `ceilMod(+4, -3) == +1`; &nbsp; and `(+4 % -3) == +1`
 *  * `ceilMod(-4, +3) == -1`; &nbsp; and `(-4 % +3) == -1`
 *
 *
 *
 *
 * @param x the dividend
 * @param y the divisor
 * @return the ceiling modulus `x - (ceilDiv(x, y) * y)`
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .ceilDiv
 * @since 18
 */
fun Math.ceilMod(x: Int, y: Int): Int {
    val r = x % y
    // if the signs are the same and modulo not zero, adjust result
    if ((x xor y) >= 0 && r != 0) {
        return r - y
    }
    return r
}

/**
 * Returns the ceiling modulus of the `long` and `int` arguments.
 *
 *
 * The ceiling modulus is `r = x - (ceilDiv(x, y) * y)`,
 * has the opposite sign as the divisor `y` or is zero, and
 * is in the range of `-abs(y) < r < +abs(y)`.
 *
 *
 *
 * The relationship between `ceilDiv` and `ceilMod` is such that:
 *
 *  * `ceilDiv(x, y) * y + ceilMod(x, y) == x`
 *
 *
 *
 * For examples, see [.ceilMod].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the ceiling modulus `x - (ceilDiv(x, y) * y)`
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .ceilDiv
 * @since 18
 */
fun Math.ceilMod(x: Long, y: Int): Int {
    // Result cannot overflow the range of int.
    return ceilMod(x, y.toLong()).toInt()
}

/**
 * Returns the ceiling modulus of the `long` arguments.
 *
 *
 * The ceiling modulus is `r = x - (ceilDiv(x, y) * y)`,
 * has the opposite sign as the divisor `y` or is zero, and
 * is in the range of `-abs(y) < r < +abs(y)`.
 *
 *
 *
 * The relationship between `ceilDiv` and `ceilMod` is such that:
 *
 *  * `ceilDiv(x, y) * y + ceilMod(x, y) == x`
 *
 *
 *
 * For examples, see [.ceilMod].
 *
 * @param x the dividend
 * @param y the divisor
 * @return the ceiling modulus `x - (ceilDiv(x, y) * y)`
 * @throws ArithmeticException if the divisor `y` is zero
 * @see .ceilDiv
 * @since 18
 */
fun Math.ceilMod(x: Long, y: Long): Long {
    val r = x % y
    // if the signs are the same and modulo not zero, adjust result
    if ((x xor y) >= 0 && r != 0L) {
        return r - y
    }
    return r
}



/**
 * Returns the mathematical absolute value of an `int` value
 * if it is exactly representable as an `int`, throwing
 * `ArithmeticException` if the result overflows the
 * positive `int` range.
 *
 *
 * Since the range of two's complement integers is asymmetric
 * with one additional negative value (JLS {@jls 4.2.1}), the
 * mathematical absolute value of [Integer.MIN_VALUE]
 * overflows the positive `int` range, so an exception is
 * thrown for that argument.
 *
 * @param  a  the argument whose absolute value is to be determined
 * @return the absolute value of the argument, unless overflow occurs
 * @throws ArithmeticException if the argument is [Integer.MIN_VALUE]
 * @see Math.abs
 * @since 15
 */
fun Math.absExact(a: Int): Int {
    if (a == Int.Companion.MIN_VALUE) throw ArithmeticException(
        "Overflow to represent absolute value of Integer.MIN_VALUE"
    )
    else return abs(a)
}


/**
 * Returns the size of an ulp of the argument.  An ulp, unit in
 * the last place, of a `double` value is the positive
 * distance between this floating-point value and the `double` value next larger in magnitude.  Note that for non-NaN
 * *x*, `ulp(-*x*) == ulp(*x*)`.
 *
 *
 * Special Cases:
 *
 *  *  If the argument is NaN, then the result is NaN.
 *  *  If the argument is positive or negative infinity, then the
 * result is positive infinity.
 *  *  If the argument is positive or negative zero, then the result is
 * `Double.MIN_VALUE`.
 *  *  If the argument is `Double.MAX_VALUE`, then
 * the result is equal to 2<sup>971</sup>.
 *
 *
 * @param d the floating-point value whose ulp is to be returned
 * @return the size of an ulp of the argument
 * @author Joseph D. Darcy
 * @since 1.5
 */
fun Math.ulp(d: Double): Double {
    var exp: Int = getExponent(d)

    return when (exp) {
        Double.MAX_EXPONENT + 1 -> abs(d)
        Double.MIN_EXPONENT - 1 -> Double.Companion.MIN_VALUE
        else -> {
            require(exp <= Double.MAX_EXPONENT && exp >= Double.MIN_EXPONENT)

            // ulp(x) is usually 2^(SIGNIFICAND_WIDTH-1)*(2^ilogb(x))
            exp -= (DoubleConsts.SIGNIFICAND_WIDTH - 1)
            if (exp >= Double.MIN_EXPONENT) {
                Math.powerOfTwoD(exp)
            } else {
                // return a subnormal result; left shift integer
                // representation of Double.MIN_VALUE appropriate
                // number of positions
                Double.longBitsToDouble(
                    1L shl (exp - (Double.MIN_EXPONENT - (DoubleConsts.SIGNIFICAND_WIDTH - 1)))
                )
            }
        }
    }
}

/**
 * Returns the size of an ulp of the argument.  An ulp, unit in
 * the last place, of a `float` value is the positive
 * distance between this floating-point value and the `float` value next larger in magnitude.  Note that for non-NaN
 * *x*, `ulp(-*x*) == ulp(*x*)`.
 *
 *
 * Special Cases:
 *
 *  *  If the argument is NaN, then the result is NaN.
 *  *  If the argument is positive or negative infinity, then the
 * result is positive infinity.
 *  *  If the argument is positive or negative zero, then the result is
 * `Float.MIN_VALUE`.
 *  *  If the argument is `Float.MAX_VALUE`, then
 * the result is equal to 2<sup>104</sup>.
 *
 *
 * @param f the floating-point value whose ulp is to be returned
 * @return the size of an ulp of the argument
 * @author Joseph D. Darcy
 * @since 1.5
 */
fun Math.ulp(f: Float): Float {
    var exp: Int =getExponent(f)

    return when (exp) {
        Float.MAX_EXPONENT + 1 -> abs(f)
        Float.MIN_EXPONENT - 1 -> Float.Companion.MIN_VALUE
        else -> {
            require(exp <= Float.MAX_EXPONENT && exp >= Float.MIN_EXPONENT)

            // ulp(x) is usually 2^(SIGNIFICAND_WIDTH-1)*(2^ilogb(x))
            exp -= (FloatConsts.SIGNIFICAND_WIDTH - 1)
            if (exp >= Float.MIN_EXPONENT) {
                Math.powerOfTwoF(exp)
            } else {
                // return a subnormal result; left shift integer
                // representation of FloatConsts.MIN_VALUE appropriate
                // number of positions
                Float.intBitsToFloat(
                    1 shl (exp - (Float.MIN_EXPONENT - (FloatConsts.SIGNIFICAND_WIDTH - 1)))
                )
            }
        }
    }
}


/**
 * Returns a floating-point power of two in the normal range.
 */
fun Math.powerOfTwoD(n: Int): Double {
    require(n >= Double.MIN_EXPONENT && n <= Double.MAX_EXPONENT)
    return Double.longBitsToDouble(
        ((n.toLong() + DoubleConsts.EXP_BIAS.toLong()) shl
                (DoubleConsts.SIGNIFICAND_WIDTH - 1))
                and DoubleConsts.EXP_BIT_MASK
    )
}

/**
 * Returns a floating-point power of two in the normal range.
 */
fun Math.powerOfTwoF(n: Int): Float {
    require(n >= Float.MIN_EXPONENT && n <= Float.MAX_EXPONENT)
    return Float.intBitsToFloat(
        ((n + FloatConsts.EXP_BIAS) shl
                (FloatConsts.SIGNIFICAND_WIDTH - 1))
                and FloatConsts.EXP_BIT_MASK
    )
}


/**
 * Returns the unbiased exponent used in the representation of a
 * `float`.  Special cases:
 *
 *
 *  * If the argument is NaN or infinite, then the result is
 * [Float.MAX_EXPONENT] + 1.
 *  * If the argument is zero or subnormal, then the result is
 * [Float.MIN_EXPONENT] - 1.
 *
 * @apiNote
 * This method is analogous to the logB operation defined in IEEE
 * 754, but returns a different value on subnormal arguments.
 *
 * @param f a `float` value
 * @return the unbiased exponent of the argument
 * @since 1.6
 */
fun  Math.getExponent(f: Float): Int {
    /*
         * Bitwise convert f to integer, mask out exponent bits, shift
         * to the right and then subtract out float's bias adjust to
         * get true exponent value
         */
    return ((Float.floatToRawIntBits(f) and FloatConsts.EXP_BIT_MASK) shr
            (FloatConsts.SIGNIFICAND_WIDTH - 1)) - FloatConsts.EXP_BIAS
}

/**
 * Returns the unbiased exponent used in the representation of a
 * `double`.  Special cases:
 *
 *
 *  * If the argument is NaN or infinite, then the result is
 * [Double.MAX_EXPONENT] + 1.
 *  * If the argument is zero or subnormal, then the result is
 * [Double.MIN_EXPONENT] - 1.
 *
 * @apiNote
 * This method is analogous to the logB operation defined in IEEE
 * 754, but returns a different value on subnormal arguments.
 *
 * @param d a `double` value
 * @return the unbiased exponent of the argument
 * @since 1.6
 */
fun  Math.getExponent(d: Double): Int {
    /*
         * Bitwise convert d to long, mask out exponent bits, shift
         * to the right and then subtract out double's bias adjust to
         * get true exponent value.
         */
    return (((Double.doubleToRawLongBits(d) and DoubleConsts.EXP_BIT_MASK) shr
            (DoubleConsts.SIGNIFICAND_WIDTH - 1)) - DoubleConsts.EXP_BIAS).toInt()
}


/**
 * Returns the fused multiply add of the three arguments; that is,
 * returns the exact product of the first two arguments summed
 * with the third argument and then rounded once to the nearest
 * `double`.
 *
 * The rounding is done using the [ ][java.math.RoundingMode.HALF_EVEN].
 *
 * In contrast, if `a * b + c` is evaluated as a regular
 * floating-point expression, two rounding errors are involved,
 * the first for the multiply operation, the second for the
 * addition operation.
 *
 *
 * Special cases:
 *
 *  *  If any argument is NaN, the result is NaN.
 *
 *  *  If one of the first two arguments is infinite and the
 * other is zero, the result is NaN.
 *
 *  *  If the exact product of the first two arguments is infinite
 * (in other words, at least one of the arguments is infinite and
 * the other is neither zero nor NaN) and the third argument is an
 * infinity of the opposite sign, the result is NaN.
 *
 *
 *
 *
 * Note that `fma(a, 1.0, c)` returns the same
 * result as (`a + c`).  However,
 * `fma(a, b, +0.0)` does *not* always return the
 * same result as (`a * b`) since
 * `fma(-0.0, +0.0, +0.0)` is `+0.0` while
 * (`-0.0 * +0.0`) is `-0.0`; `fma(a, b, -0.0)` is
 * equivalent to (`a * b`) however.
 *
 * @apiNote This method corresponds to the fusedMultiplyAdd
 * operation defined in IEEE 754.
 *
 * @param a a value
 * @param b a value
 * @param c a value
 *
 * @return (*a*&nbsp;&nbsp;*b*&nbsp;+&nbsp;*c*)
 * computed, as if with unlimited range and precision, and rounded
 * once to the nearest `double` value
 *
 * @since 9
 */
fun Math.fma(a: Double, b: Double, c: Double): Double {
    /*
         * Infinity and NaN arithmetic is not quite the same with two
         * roundings as opposed to just one so the simple expression
         * "a * b + c" cannot always be used to compute the correct
         * result.  With two roundings, the product can overflow and
         * if the addend is infinite, a spurious NaN can be produced
         * if the infinity from the overflow and the infinite addend
         * have opposite signs.
         */

    // First, screen for and handle non-finite input values whose
    // arithmetic is not supported by BigDecimal.

    if (Double.isNaN(a) || Double.isNaN(b) || Double.isNaN(c)) {
        return Double.NaN
    } else { // All inputs non-NaN
        val infiniteA = Double.isInfinite(a)
        val infiniteB = Double.isInfinite(b)
        val infiniteC = Double.isInfinite(c)
        val result: Double

        if (infiniteA || infiniteB || infiniteC) {
            if (infiniteA && b == 0.0 ||
                infiniteB && a == 0.0
            ) {
                return Double.NaN
            }
            val product = a * b
            if (Double.isInfinite(product) && !infiniteA && !infiniteB) {
                // Intermediate overflow; might cause a
                // spurious NaN if added to infinite c.
                require(Double.isInfinite(c))
                return c
            } else {
                result = product + c
                require(!Double.isFinite(result))
                return result
            }
        } else { // All inputs finite
            val product = (BigDecimal.fromDouble(a)).multiply(BigDecimal.fromDouble(b))
            if (c == 0.0) { // Positive or negative zero
                // If the product is an exact zero, use a
                // floating-point expression to compute the sign
                // of the zero final result. The product is an
                // exact zero if and only if at least one of a and
                // b is zero.
                if (a == 0.0 || b == 0.0) {
                    return a * b + c
                } else {
                    // The sign of a zero addend doesn't matter if
                    // the product is nonzero. The sign of a zero
                    // addend is not factored in the result if the
                    // exact product is nonzero but underflows to
                    // zero; see IEEE-754 2008 section 6.3 "The
                    // sign bit".
                    return product.doubleValue()
                }
            } else {
                return product.add(BigDecimal.fromDouble(c)).doubleValue()
            }
        }
    }
}

/**
 * Returns the fused multiply add of the three arguments; that is,
 * returns the exact product of the first two arguments summed
 * with the third argument and then rounded once to the nearest
 * `float`.
 *
 * The rounding is done using the [ ][java.math.RoundingMode.HALF_EVEN].
 *
 * In contrast, if `a * b + c` is evaluated as a regular
 * floating-point expression, two rounding errors are involved,
 * the first for the multiply operation, the second for the
 * addition operation.
 *
 *
 * Special cases:
 *
 *  *  If any argument is NaN, the result is NaN.
 *
 *  *  If one of the first two arguments is infinite and the
 * other is zero, the result is NaN.
 *
 *  *  If the exact product of the first two arguments is infinite
 * (in other words, at least one of the arguments is infinite and
 * the other is neither zero nor NaN) and the third argument is an
 * infinity of the opposite sign, the result is NaN.
 *
 *
 *
 *
 * Note that `fma(a, 1.0f, c)` returns the same
 * result as (`a + c`).  However,
 * `fma(a, b, +0.0f)` does *not* always return the
 * same result as (`a * b`) since
 * `fma(-0.0f, +0.0f, +0.0f)` is `+0.0f` while
 * (`-0.0f * +0.0f`) is `-0.0f`; `fma(a, b, -0.0f)` is
 * equivalent to (`a * b`) however.
 *
 * @apiNote This method corresponds to the fusedMultiplyAdd
 * operation defined in IEEE 754.
 *
 * @param a a value
 * @param b a value
 * @param c a value
 *
 * @return (*a*&nbsp;&nbsp;*b*&nbsp;+&nbsp;*c*)
 * computed, as if with unlimited range and precision, and rounded
 * once to the nearest `float` value
 *
 * @since 9
 */
fun Math.fma(a: Float, b: Float, c: Float): Float {
    if (Float.isFinite(a) && Float.isFinite(b) && Float.isFinite(c)) {
        if (a.toDouble() == 0.0 || b.toDouble() == 0.0) {
            return a * b + c // Handled signed zero cases
        } else {
            return (BigDecimal.fromDouble(a.toDouble() * b.toDouble()) // Exact multiply
                .add(BigDecimal.fromDouble(c.toDouble()))) // Exact sum
                .floatValue() // One rounding
            // to a float value
        }
    } else {
        // At least one of a,b, and c is non-finite. The result
        // will be non-finite as well and will be the same
        // non-finite value under double as float arithmetic.
        return fma(a.toDouble(), b.toDouble(), c.toDouble()).toFloat()
    }
}
